<?php


class SEA {

// South East Asian shaper

// sea_category
    const OT_X = 0;
    const OT_C    = 1;
    const OT_IV   = 2;  # Independent Vowel
    const OT_T    = 3;  # Tone Marks
    const OT_H    = 4;  # Halant
    const OT_A    = 10; # Anusvara
    const OT_GB   = 12; # Generic Base	(OT_DOTTEDCIRCLE in Indic)
    const OT_CM   = 17; # Consonant Medial
    const OT_MR   = 22; # Medial Ra
    const OT_VAbv = 26;
    const OT_VBlw = 27;
    const OT_VPre = 28;
    const OT_VPst = 29;
// ? From Indic categories
    const OT_ZWNJ = 5;
    const OT_ZWJ = 6;
    const OT_M = 7;
    const OT_SM = 8;
    const OT_VD = 9;
    const OT_NBSP = 11;
    const OT_RS = 13;
    const OT_Coeng = 14;
    const OT_Repha = 15;
    const OT_Ra = 16;

// Based on sea_category used to make string to find syllables
// OT_ to string character (using e.g. OT_C from INDIC) hb-ot-shape-complex-sea-private.hh
    public static $sea_category_char = array(
        'x',
        'C',
        'V',
        'T',
        'H',
        'x',
        'x',
        'x',
        'x',
        'x',
        'A',
        'x',
        'G',
        'x',
        'x',
        'x',
        'x',
        'M',
        'x',
        'x',
        'x',
        'x',
        'R',
        'x',
        'x',
        'x',
        'a',
        'b',
        'p',
        't',
    );


    /* Visual positions in a syllable from left to right. */
// sea_position
    const POS_START = 0;

    const POS_RA_TO_BECOME_REPH = 1;
    const POS_PRE_M = 2;
    const POS_PRE_C = 3;

    const POS_BASE_C = 4;
    const POS_AFTER_MAIN = 5;

    const POS_ABOVE_C = 6;

    const POS_BEFORE_SUB = 7;
    const POS_BELOW_C = 8;
    const POS_AFTER_SUB = 9;

    const POS_BEFORE_POST = 10;
    const POS_POST_C = 11;
    const POS_AFTER_POST = 12;

    const POS_FINAL_C = 13;
    const POS_SMVD = 14;

    const POS_END = 15;



    public static function set_sea_properties(&$info, $scriptblock ) {
        $u = $info['uni'];
        $type = self::sea_get_categories($u);
        $cat = ($type & 0x7F);
        $pos = ($type >> 8);

        /*
        * Re-assign category
        */
        // Medial Ra
        if ($u == 0x1A55 || $u == 0xAA34) { $cat = self::OT_MR; }

        /*
        * Re-assign position.
        */
        if ($cat == self::OT_M) {	// definitely "OT_M" in HarfBuzz - although this does not seem to have been defined ? should be OT_MR
            switch ($pos) {
                case self::POS_PRE_C:	$cat = self::OT_VPre; break;
                case self::POS_ABOVE_C:	$cat = self::OT_VAbv; break;
                case self::POS_BELOW_C:	$cat = self::OT_VBlw; break;
                case self::POS_POST_C:	$cat = self::OT_VPst; break;
            }
        }

        $info['sea_category'] = $cat;
        $info['sea_position'] = $pos;
    }

// syllable_type
    const CONSONANT_SYLLABLE = 0;
    const BROKEN_CLUSTER = 1;
    const NON_SEA_CLUSTER = 2;



    public static function set_syllables(&$o, $s, &$broken_syllables) {
        $ptr = 0;
        $syllable_serial = 1;
        $broken_syllables = false;
        while($ptr < strlen($s)) {
            $match = '';
            $syllable_length = 1;
            $syllable_type = self::NON_SEA_CLUSTER ;

            // CONSONANT_SYLLABLE Consonant syllable
            if (preg_match('/^(C|V|G)(p|a|b|t|HC|M|R|T|A)*/', substr($s,$ptr), $ma)) {
                $syllable_length = strlen($ma[0]);
                $syllable_type = self::CONSONANT_SYLLABLE ;
            }
            // BROKEN_CLUSTER syllable
            else if (preg_match('/^(p|a|b|t|HC|M|R|T|A)+/', substr($s,$ptr), $ma)) {
                $syllable_length = strlen($ma[0]);
                $syllable_type = self::BROKEN_CLUSTER ;
                $broken_syllables = true;
            }

            for ($i = $ptr; $i < $ptr+$syllable_length; $i++) { $o[$i]['syllable'] = ($syllable_serial << 4) | $syllable_type; }
            $ptr += $syllable_length ;
            $syllable_serial++;
            if ($syllable_serial == 16) $syllable_serial = 1;
        }
    }

    public static function initial_reordering(&$info, $GSUBdata, $broken_syllables, $scriptblock, $dottedcircle) {

        if ($broken_syllables && $dottedcircle) { self::insert_dotted_circles ($info, $dottedcircle); }

        $count = count($info);
        if (!$count) return;
        $last = 0;
        $last_syllable = $info[0]['syllable'];
        for ($i = 1; $i < $count; $i++) {
            if ($last_syllable != $info[$i]['syllable']) {
                self::initial_reordering_syllable ($info, $GSUBdata, $scriptblock, $last, $i);
                $last = $i;
                $last_syllable = $info[$last]['syllable'];
            }
        }
        self::initial_reordering_syllable($info, $GSUBdata, $scriptblock, $last, $count);
    }

    public static function insert_dotted_circles(&$info, $dottedcircle) {
        $idx = 0;
        $last_syllable = 0;
        while ($idx < count($info)) {
            $syllable = $info[$idx]['syllable'];
            $syllable_type = ($syllable & 0x0F);
            if ($last_syllable != $syllable && $syllable_type == self::BROKEN_CLUSTER) {
                $last_syllable = $syllable;
                $dottedcircle[0]['syllable'] = $info[$idx]['syllable'];
                array_splice($info, $idx, 0, $dottedcircle);
            }
            else
                $idx++;
        }
    }




    public static function initial_reordering_syllable (&$info, $GSUBdata, $scriptblock, $start, $end) {
        /* broken_cluster: We already inserted dotted-circles, so just call the standalone_cluster. */

        $syllable_type = ($info[$start]['syllable'] & 0x0F);
        if ($syllable_type==self::NON_SEA_CLUSTER ) { return; }
        if ($syllable_type==self::BROKEN_CLUSTER) {
            /* For dotted-circle, this is what Uniscribe does:
            * If dotted-circle is the last glyph, it just does nothing. */
            if ($info[$end - 1]['sea_category'] == self::OT_GB) {
                return;
            }
        }

        $base = $start;
        $i = $start;
        for (; $i < $base; $i++)
            $info[$i]['sea_position'] = self::POS_PRE_C;
        if ($i < $end) {
            $info[$i]['sea_position'] = self::POS_BASE_C;
            $i++;
        }
        for (; $i < $end; $i++) {
            if ($info[$i]['sea_category'] == self::OT_MR) { /* Pre-base reordering */
                $info[$i]['sea_position'] = self::POS_PRE_C;
                continue;
            }
            if ($info[$i]['sea_category'] == self::OT_VPre) { /* Left matra */
                $info[$i]['sea_position'] = self::POS_PRE_M;
                continue;
            }
            $info[$i]['sea_position'] = self::POS_AFTER_MAIN;
        }

        /* Sit tight, rock 'n roll! */
        self::bubble_sort ($info, $start, $end - $start);

    }

    public static function final_reordering (&$info, $GSUBdata, $scriptblock) {
        $count = count($info);
        if (!$count) return;
        $last = 0;
        $last_syllable = $info[0]['syllable'];
        for ($i = 1; $i < $count; $i++) {
            if ($last_syllable != $info[$i]['syllable']) {
                self::final_reordering_syllable ($info, $GSUBdata, $scriptblock, $last, $i);
                $last = $i;
                $last_syllable = $info[$last]['syllable'];
            }
        }
        self::final_reordering_syllable ($info, $GSUBdata, $scriptblock, $last, $count);

    }

    public static function final_reordering_syllable (&$info, $GSUBdata, $scriptblock, $start, $end) {
        /*
        * Nothing to do here at present!
        */

    }




    public static $sea_table = array(

        /* New Tai Lue  (1980..19DF) */

        /* 1980 */  3841,  3841,  3841,  3841,  3841,  3841,  3841,  3841,
        /* 1988 */  3841,  3841,  3841,  3841,  3841,  3841,  3841,  3841,
        /* 1990 */  3841,  3841,  3841,  3841,  3841,  3841,  3841,  3841,
        /* 1998 */  3841,  3841,  3841,  3841,  3841,  3841,  3841,  3841,
        /* 19A0 */  3841,  3841,  3841,  3841,  3841,  3841,  3841,  3841,
        /* 19A8 */  3841,  3841,  3841,  3841,  3840,  3840,  3840,  3840,
        /* 19B0 */  2823,  2823,  2823,  2823,  2823,  775,  775,  775,
        /* 19B8 */  2823,  2823,  775,  2823,  2823,  2823,  2823,  2823,
        /* 19C0 */  2823, 3857, 3857, 3857, 3857, 3857, 3857, 3857,
        /* 19C8 */ 3843, 3843,  3840,  3840,  3840,  3840,  3840,  3840,
        /* 19D0 */  3840,  3840,  3840,  3840,  3840,  3840,  3840,  3840,
        /* 19D8 */  3840,  3840,  3840,  3840,  3840,  3840,  3840,  3840,

        /* Tai Tham  (1A20..1AAF) */

        /* 1A20 */  3841,  3841,  3841,  3841,  3841,  3841,  3841,  3841,
        /* 1A28 */  3841,  3841,  3841,  3841,  3841,  3841,  3841,  3841,
        /* 1A30 */  3841,  3841,  3841,  3841,  3841,  3841,  3841,  3841,
        /* 1A38 */  3841,  3841,  3841,  3841,  3841,  3841,  3841,  3841,
        /* 1A40 */  3841,  3841,  3841,  3841,  3841,  3841,  3841,  3841,
        /* 1A48 */  3841,  3841,  3841,  3841,  3841, 3842, 3842, 3842,
        /* 1A50 */3842, 3842, 3842,  3841,  3841, 3857, 3857, 3857,
        /* 1A58 */ 3857, 3857, 3857, 3857, 3857, 3857, 3857,  3840,
        /* 1A60 */  3844,  2823,  1543,  2823,  2823,  1543,  1543,  1543,
        /* 1A68 */  1543,  2055,  2055,  1543,  2055,  2823,  775,  775,
        /* 1A70 */  775,  775,  775,  1543,  1543, 3843, 3843, 3843,
        /* 1A78 */ 3843, 3843,  3840,  3840,  3840,  3840,  3840,  3840,
        /* 1A80 */  3840,  3840,  3840,  3840,  3840,  3840,  3840,  3840,
        /* 1A88 */  3840,  3840,  3840,  3840,  3840,  3840,  3840,  3840,
        /* 1A90 */  3840,  3840,  3840,  3840,  3840,  3840,  3840,  3840,
        /* 1A98 */  3840,  3840,  3840,  3840,  3840,  3840,  3840,  3840,
        /* 1AA0 */  3840,  3840,  3840,  3840,  3840,  3840,  3840,  3840,
        /* 1AA8 */  3840,  3840,  3840,  3840,  3840,  3840,  3840,  3840,

        /* Cham  (AA00..AA5F) */

        /* AA00 */ 3842, 3842, 3842, 3842, 3842, 3842,  3841,  3841,
        /* AA08 */  3841,  3841,  3841,  3841,  3841,  3841,  3841,  3841,
        /* AA10 */  3841,  3841,  3841,  3841,  3841,  3841,  3841,  3841,
        /* AA18 */  3841,  3841,  3841,  3841,  3841,  3841,  3841,  3841,
        /* AA20 */  3841,  3841,  3841,  3841,  3841,  3841,  3841,  3841,
        /* AA28 */  3841,  1543,  1543,  1543,  1543,  2055,  1543,  775,
        /* AA30 */  775,  1543,  2055, 3857, 3857, 3857, 3857,  3840,
        /* AA38 */  3840,  3840,  3840,  3840,  3840,  3840,  3840,  3840,
        /* AA40 */ 3857, 3857, 3857, 3857, 3857, 3857, 3857, 3857,
        /* AA48 */ 3857, 3857, 3857, 3857, 3857, 3857,  3840,  3840,
        /* AA50 */  3840,  3840,  3840,  3840,  3840,  3840,  3840,  3840,
        /* AA58 */  3840,  3840,  3840,  3840,  3840,  3840,  3840,  3840,

    );



    public static function sea_get_categories ($u) {
        if (0x1980 <= $u && $u <= 0x19DF) return self::$sea_table[$u - 0x1980];	// offset 0 for New Tai Lue
        if (0x1A20 <= $u && $u <= 0x1AAF) return self::$sea_table[$u - 0x1A20 + 96];	// offset for Tai Tham
        if (0xAA00 <= $u && $u <= 0xAA5F) return self::$sea_table[$u - 0xAA00 + 96 + 144];		// Cham
        if ($u == 0x00A0) return 3851;	// (ISC_CP | (IMC_x << 8))
        if ($u == 0x25CC) return 3851;	// (ISC_CP | (IMC_x << 8))
        return 3840;	// (ISC_x | (IMC_x << 8))
    }


    public static function bubble_sort(&$arr, $start, $len) {
        if ($len<2) { return;}
        $k = $start+$len-2;
        while ($k >= $start) {
            for ($j=$start; $j<=$k; $j++) {
                if ($arr[$j]['sea_position'] > $arr[$j + 1]['sea_position']) {
                    $t = $arr[$j];
                    $arr[$j] = $arr[$j + 1];
                    $arr[$j + 1] = $t;
                }
            }
            $k--;
        }
    }




}	// end Class

?>